<!DOCTYPE html>
<html lang="zh-cn">
  <head>
    <meta charset="UTF-8">
    <title>Sucha's Blog - Archive for January, 2021</title>
    <meta name="generator" content="MarkdownProjectCompositor.lua">
    <meta name="author" content="Sucha">
    <meta name="keywords" content="suchang, programming, Linux, Lua">
    <meta name="description" content="Sucha's blog">
    <link rel="shortcut icon" href="../images/ico.png">
    <link rel="stylesheet" type="text/css" href="../styles/blog.css">
    <link rel="stylesheet" type="text/css" href="../styles/prism.min.css">
    <style id="site_theme"></style>
  </head>
  <body>
    <div id="body">
      <div id="text">
	   <!-- Page published by cmark-gfm begins here --><h1>Sucha's Blog ~ Archive for January, 2021</h1>
<p><a id="p0"></a></p>
<div class="date">21年1月24日 周日 22:52</div>
<h2>Swift(2)</h2>
<p>又写了一个多月的 Swift，除了刚开始时的修修补补，慢慢扩展到页面框架部分，不过这个部分我还是不够熟悉，一部分在于我还是沿用项目之前的 ViewModel，另外一部分，很多业务细节还没能深入理解。</p>
<p>虽然这次的标题是 Swift(2)，实际内容大部分是 RxSwift 及 ViewController 的结合，以及一些 UI 层级的实践心得。</p>
<h3>页面框架</h3>
<p>先说一下我们的网络层，搭建了类似 Moya 的网络层框架，基本上是够用了，但相比之前用的 Model 网络层，这里浅浅的封装，就不会有单独的 model 管理器了。简洁是简洁了很多，但是数据层面转换和粘合就得在 ViewController 处理，带来了 ViewController 的膨胀。比如因为用了 RxSwift，同一份数据，有时需要多次 transform，subscribe，这些可都在 ViewController 里面。</p>
<p>我曾经想过这些代码放在 ViewModel 里面会不会好一些，但我上次也许也有提到，这里的 ViewModel，更像是一个 Driver.System 的状态机，初始状态是 initial，之后是 Signal -&gt; Driver 的状态切换，通过一个 reduce 函数检查 ViewModel 绑定的 struct 结构来判断到底应该 loading、setResult 还是其他，如果弄懂了这个，页面的状态处理就很清晰了。</p>
<p>只是这个 ViewModel 只负责将 app 外的数据，transform 为页面状态数据，不涉及到 UI element 数据的 transform，这是跟之前 ViewModel 的区别。ViewModel 带回来的页面状态数据，最终会在 ViewController 中 transform 并 binding、drive UI element，这个过程有时需要做很多的粘合工作，流程也会相当复杂。</p>
<p>另外，UI 层级方面，已有较为复杂的页面，通常 UI View 就好几层，一层一层的 UIStackView 包裹里面的自定义子 view，将需要传递的数据链路，通过 binder 传递出来，即便是 View，都跟 RxSwift 框架直接绑定了。</p>
<p>说一下对这个的理解，我觉得是没必要，因为这个 binder 或者 driver，其实都只算是单向的数据传递，传递出来的时候用就好，传递进去的时候，感觉其实没那么必要的了。</p>
<p>以上提到的，觉得最复杂的还是 ViewModel 及其 Driver.System，基本上抄了几遍，这段时间除了 K 线 ViewController 没有修改过，其他页面多少接触了一点。</p>
<p>目前的页面框架工作得还可以，通用性也还行，只是觉得业务复杂度还是太靠近 ViewController 了。而且当前的框架，ViewController 绑定 TableView 被封装得很厚重，仅支持单种 cell 的 TableView，不知道当时这个模版构建的时候是怎么想的。</p>
<p>还有 TableView 的 DataSource，依赖了 RxDataSource，在 TableView 上，其实不支持 HeaderView 的数据流绑定，还得用回系统提供的 delegate 方式，不伦不类的，还不如全部都用系统的方式呢，因为这个模版框架，将 createCell 的部分，dataSOurce 的数据创建部分，放在了 TableViewCell 里面，这个我是非常不认同的。</p>
<p>我总觉得 Cell 有复用的可能，两者结合不应该那么紧密，即便有 protocol 作为粘合，有时候还是觉得抽象过了，没必要。</p>
<ul>
<li>业务强依赖 RxSwift，不够纯粹，不适合封装带给非 RxSwift 的第三方使用了</li>
<li>页面状态处理优秀，但带来未解决的问题，是业务复杂度又堆积到了 ViewController，除了 VC 臃肿之外，还有就是其实没有业务子 View 的划分，因为其输入输出都在 ViewController，边界不明确</li>
<li>TableView 的页面模版虽然能处理当前业务的问题，但设计之初未考虑到各种其他情况，不够灵活</li>
</ul>
<p>这段时间还将 Carthage 的库切换到了静态库上，模拟器运行节省了不少时间，但这个改进在真机上几乎觉察不出来。</p>
<p>做了这部分的心得是，我们依赖的第三方库实在是太多了，这也造成了我们启动慢，因为打包好的 App 就有 150M+。比如我们的数据库，为何要用 Realm 呢，是因为 ORM 方便吗，还是因为它更快，总觉得还不如用系统提供的 SQLite3 好了，或者浅浅地封装一层一层，比如 GRDB 就好。</p>
<p>因为这个库太大了，打包的时候都可以看到打的慢。</p>
<h3>UI 层级</h3>
<p>学习使用 UIStackView，其实这个我在几年前就有做了一个基于 frame 计算的 StackView 框架，可惜是公司财产，不能开源出来（显然我也没有源码）。</p>
<p>UIStackView 使用有些前提，一方面需要 addArrangedView 添加进来，但是如果只是删除 ArrangedView，其实还在 subviews 里面。</p>
<p>另一方面，每个 view 需要有自己的 size，以及 hugging、compress 优先级，要不感觉最后计算宽度大小的时候，总是不准确。</p>
<p>还有学会了在 tableview 的 header view 上面放置复杂的 UI 层级，这个 tableView 的 header view，不支持 AutoLayout，只能是指定 frame 高度。</p>
<p>但是呢，又能通过将这个 view systemLayoutSizeFittingSize 拿到高度，设置给 frame 来确定这个 tableview header view 的高度。</p>
<p>还有 table view 的 cell 是可以不指定高度的，通过 AutoLayout 计算出来，但是呢，collection view 不可以。</p>
<p>这段时间因为对这些 UI 规则不熟悉，浪费了很多时间，有时想着，还不如我来设置 frame 呢，工作早就做完了。</p>
<p>另外，复杂一点的首页等等 ViewController，各种 NSLayout warning，见怪不怪，设置符合 AutoLayout 的规则，太难了。</p>
<p>哦，对了，我们有两套 layout 的库，一个是 SnapKit，一个是 TinyConstraint，常常是混用的状态，真的有点混乱。</p>
<p>--</p>
<p>大概先这样吧。</p>
<div class="category"><a href="CategoryProgramming.html">CategoryProgramming</a> / <a href="2021-01.html#p0">Permalink</a> / <a href="https://github.com/lalawue/homepage/discussions/categories/blog" target="_blank">Discussion</a></div>
<!-- Page published by cmark-gfm ends here -->
  <div id="foot">2004-<script>var d = new
	Date();document.write(d.getFullYear())</script> &copy;
	Sucha. Powered by MarkdownProjectCompositor.
  </div>
  </div><!-- text -->
  <div id="sidebar">
  </div><!-- sidebar -->
  <script src="../js/prism.min.js" async="async"></script>
  <script src="../js/blog_sidebar.js"></script>
  </div> <!-- body -->
</body>
</html>